/*****************************************************
*                                                                          
*               COPYRIGHT (c) 2017-2019 Hargic Corporation                 
*                         All Rights Reserved                              
*                                                                          
* The source code contained or described herein and all documents          
* related to the source code ("Material") are owned by Hargic            
* Corporation or its licensors.  Title to the Material remains             
* with Hargic Corporation or its suppliers and licensors.                  
*                                                                          
* The Material is protected by worldwide copyright and trade secret        
* laws and treaty provisions. No part of the Material may be used,         
* copied, reproduced, modified, published, uploaded, posted, transmitted,  
* distributed, or disclosed in any way except in accordance with the       
* applicable license agreement.                                            
*                                                                          
* No license under any patent, copyright, trade secret or other            
* intellectual property right is granted to or conferred upon you by       
* disclosure or delivery of the Materials, either expressly, by            
* implication, inducement, estoppel, except in accordance with the         
* applicable license agreement.                                            
*                                                                          
* Unless otherwise agreed by Hargic in writing, you may not remove or      
* alter this notice or any other notice embedded in Materials by Hargic    
* or Hargic's suppliers or licensors in any way.                           
*                                                                          
** Author: shengda.huang
** Date: 2019-02-21
** todo: 1. 根据 key 字母排序，采用二分法查找
**       2. 根据引用频繁度，将频繁操作的 key 往前移（双向链表），采用顺序查找
*
*****************************************************/

/***** Include files ********************************/
#include "kv.h"
#include "kv_adapter.h"
#include <stdio.h>
#include <string.h>

/***** Defines **************************************/

/***** Define structure *****************************/

/***** Define constant ******************************/

/***** Define global ********************************/

/***** Define Prototype of functions ****************/

/**
 * @brief
 * @param  None
 * @retval None
 */
void kv_init(kv_obj_t *_this)
{
    _this->top = NULL;
    dymem_heap_init(&_this->pool, _this->heap, _this->heap + _this->heap_size);
}

static void kv_dump_node(const char *desc, struct kv_list *node)
{
    kv_log("%s", desc);

    if (node == NULL)
    {
        kv_log_err("node is NULL");
        return ;
    }

    kv_log("node %p", node);
    kv_log("next %p", node->next);
    kv_log("prev %p", node->prev);
    kv_log("key size %d name %.*s", node->k_len, node->k_len, node->data);
    kv_hex_dump("value", node->data + node->k_len, node->v_len);
}

static void kv_dump_list(const char *desc, kv_obj_t *_this)
{
    kv_log("%s", desc);

    if (_this == NULL)
    {
        kv_log_err("kv_obj is NULL");
        return ;
    }

    struct kv_list *node = _this->top;

    while (node != NULL)
    {
        kv_log("node %p", node);
        kv_log("next %p", node->next);
        kv_log("prev %p", node->prev);
        kv_log("key size %d name %.*s", node->k_len, node->k_len, node->data);
        kv_hex_dump("value", node->data + node->k_len, node->v_len);

        node = node->next;
    }
}

static struct kv_list *kv_find(kv_obj_t *_this, const char *key)
{
    struct kv_list *node = _this->top;
    while (node != NULL)
    {
        if (node->k_len == strlen(key))
        {
            if (strncmp((const char *)node->data, key, node->k_len) == 0)
                return node;
        }
        node = node->next;
    }
    return NULL;
}

i32 kv_set(kv_obj_t *_this, const char *key, void *value, u32 size)
{
    if (strlen(key) >= KV_KEY_NAME_LEN_MAX)
        return -KV_E_K_EXCEEDS;

    struct kv_list *node;
    // first check is this key exit?
    if ((node = kv_find(_this, key)) != NULL)
    {
        kv_log("edit key %s node %p", key, node);
        // edit
        node = dymem_realloc(&_this->pool, node, sizeof(struct kv_list) + strlen(key) + size);
        if (node == NULL)
        {
            // do not change this node
            return -KV_E_NO_SPACE;
        }
        kv_log("realloc key %s success, new node %p", key, node);
        // kv_dump_node("defore edit", node);

        struct kv_list *n_part;
        
        // update doubly linked list
        if (node->prev == NULL) // this is top node
        {
            _this->top = node;

            if (node->next != NULL)
            {
                n_part = node->next;
                n_part->prev = node;
            }
        }
        else
        {
            if (node->next != NULL)
            {
                n_part = node->next;
                n_part->prev = node;
            }

            if (node->prev != NULL)
            {
                n_part = node->prev;
                n_part->next = node;
            }
        }

        node->k_len = strlen(key);
        node->v_len = size;

        memcpy(node->data + node->k_len, value, size);
        kv_log("copy value size %d success", size);
    }
    else
    {
        kv_log("add key %s", key);
        // add
        node = dymem_calloc(&_this->pool, sizeof(struct kv_list) + strlen(key) + size, sizeof(u8));
        if (node == NULL)
        {
            return -KV_E_NO_SPACE;
        }

        // add new node at top
        node->k_len = strlen(key);
        node->v_len = size;
        memcpy(node->data, key, node->k_len);
        memcpy(node->data + node->k_len, value, size);

        if (_this->top == NULL)
        {
            _this->top = node;
        }
        else
        {
            struct kv_list *n_part = _this->top;

            node->prev = NULL;
            node->next = n_part;

            n_part->prev = node;

            _this->top = node;
        }
    }
    kv_dump_list("kv_set", _this);
    dymem_list(&_this->pool);
    return KV_OK;
}

i32 kv_get(kv_obj_t *_this, const char *key, void *value, u32 maxsize)
{
    if (strlen(key) >= KV_KEY_NAME_LEN_MAX)
        return -KV_E_K_EXCEEDS;

    struct kv_list *node;
    // first check is this key exit?
    if ((node = kv_find(_this, key)) == NULL)
    {
        return -KV_E_NOT_FIND;
    }
    else
    {
        if (node->v_len > maxsize)
        {
            return -KV_E_NO_SPACE;
        }

        memcpy(value, node->data + node->k_len, node->v_len);
    }
    return node->v_len;
}

i32 kv_del(kv_obj_t *_this, const char *key)
{
    if (strlen(key) >= KV_KEY_NAME_LEN_MAX)
        return -KV_E_K_EXCEEDS;

    struct kv_list *node;
    // first check is this key exit?
    if ((node = kv_find(_this, key)) == NULL)
    {
        return -KV_E_NOT_FIND;
    }
    else
    {
        struct kv_list *n_part;

        // kv_dump_node("defore del", node);
        if (node == _this->top) // this is top node, we need change top pointer
        {
            if (node->next == NULL)
            {
                _this->top = NULL;
            }
            else
            {
                n_part = node->next;

                n_part->prev = NULL;
                _this->top = n_part;
            }
        }
        else
        {
            if (node->next == NULL) // this is last node
            {
                n_part = node->prev;

                n_part->next = NULL;
            }
            else
            {
                n_part = node->prev;
                n_part->next = node->next;

                n_part = node->next;
                n_part->prev = node->prev;
            }
        }

        dymem_free(&_this->pool, node);
    }
    kv_dump_list("kv_del", _this);
    dymem_list(&_this->pool);
    return KV_OK;
}

/* End of file */



